/******************************************************************************************************
Copyright (C) 2010 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals print, Utl*/
/*jslint plusplus: true, nomen: true, indent: 4*/

// String utilities object
var Str = {};

// Replaces all whitespace symbols (\n, \r, \t) with spaces.
// If the result string contains spaces then quote it.
// The function is useful e.g. when generating a CSV output of spectral titles.

Str.escapeRegExp = function escapeRegExp(aStr) {
	"use strict";

	aStr = aStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

	return aStr;
};

Str.linifyAndQuote = function linifyAndQuote(aStr, aSeparator) {
	"use strict";

	if (aSeparator !== undefined) {
		aSeparator = Str.escapeRegExp(aSeparator);
		aSeparator = new RegExp(aSeparator, "g");
		aStr = aStr.replace(aSeparator, " ");
	}
	aStr = aStr.replace(/\s/g, " ");
	if (aStr.indexOf(" ") !== -1) {
		aStr = aStr.replace(/"/g, "'");
		if (aStr.startsWith("'") && aStr.endsWith("'")) {
			aStr[0] = '"';
			aStr[aStr.length - 1] = '"';
		} else {
			aStr = '"' + aStr + '"';
		}
	}

	return aStr;
};

//This function removes the numbers from the parameter aStr
Str.removeNumbers = function removeNumbers(aStr) {
	"use strict";

	return aStr.replace(/\d/g, "");
};

Str.replaceWithHTMLTag = function replaceWithHTMLTag(aString, aRE, aTemplate) {
	"use strict";
	var retString = '',
		matched = '',
		currentToken,
		lastIndex = 0;

	do {
		matched = aRE.exec(aString);
		if (matched !== null) {
			retString += aString.substring(lastIndex, matched.index);
			currentToken = matched.toString();
			retString += currentToken.replace((new RegExp(currentToken, 'g')), aTemplate.arg(currentToken));
			lastIndex = matched.index + currentToken.length;
		}
	} while (matched !== null);
	if (lastIndex < aString.length) {
		retString += aString.substring(lastIndex, aString.length);
	}
	return retString;
};

Str.solventStr2Html = function solventStr2Html(aSolvent) {
	"use strict";

	var extendedRE = new RegExp("-[Dd]\\d*$", "g"),
		extended = '',
		extendedMatch = extendedRE.exec(aSolvent),
		base,
		mixtures = aSolvent.split("/"),
		i,
		stringMixture = '';

	//Single solvent
	if (mixtures.length === 1) {
		if (extendedMatch !== null) {
			extended = extendedMatch.toString();
			extended = Str.replaceWithHTMLTag(extended, new RegExp("[Dd]", "g"), "<i>%1</i>");
			extended = Str.replaceWithHTMLTag(extended, new RegExp("\\d+", "g"), "<sub>%1</sub>");
		}
		base = aSolvent.replace(extendedRE, '');
		base = Str.replaceWithHTMLTag(base, new RegExp("\\d+", "g"), "<sub>%1</sub>");
		stringMixture = base + extended;

	//Mixture solvent
	} else {
		for (i = 0; i < mixtures.length; i++) {
			base = '';
			extended = '';
			aSolvent = mixtures[i];
			if (aSolvent !== undefined && aSolvent.toString().trim() !== "") {
				if (extendedMatch !== null) {
					extended = extendedMatch.toString();
					extended = Str.replaceWithHTMLTag(extended, new RegExp("[Dd]", "g"), "<i>%1</i>");
					extended = Str.replaceWithHTMLTag(extended, new RegExp("\\d+", "g"), "<sub>%1</sub>");
				}
				base = aSolvent.replace(extendedRE, '');
				base = Str.replaceWithHTMLTag(base, new RegExp("\\d+", "g"), "<sub>%1</sub>");
				stringMixture += (base + extended);
				if (i < mixtures.length - 1) {
					stringMixture += "/";
				}
			}
		}
		if (stringMixture[stringMixture.length - 1] === "/") {
			stringMixture = stringMixture.substring(0, stringMixture.length - 1);
		}
	}
	return stringMixture;
};

Str.firstNotEmptyString = function firstNotEmptyString() {
	"use strict";

	var i;

	for (i = 0; i < arguments.length; i++) {
		if (arguments[i] !== undefined && arguments[i].length) {
			return arguments[i];
		}
	}
	return "";
};

Str.unBracket = function unBracket(aStr, aLeft, aRight) {
	"use strict";

	if (aStr.length > 1 && aStr[0] === aLeft && aStr[aStr.length - 1] === aRight) {
		return aStr.slice(1, aStr.length - 1);
	}
	return aStr;
};

Str.date2ISOString = function date2ISOString(aDate, aWithMSeconds, aHMSeparator) {
	"use strict";

	if (aDate === undefined) {
		aDate = new Date();
	}

	if (aHMSeparator === undefined) {
		aHMSeparator = ":";
	}

	var hms = "%1%2%3%2%4".arg(aDate.getHours(), 2, "0").arg(aHMSeparator).arg(aDate.getMinutes(), 2, "0").arg(aDate.getSeconds(), 2, "0"),
		ymd = "%1-%2-%3".arg(
		aDate.getFullYear()).arg(
		aDate.getMonth() + 1, 2, "0").arg(
		aDate.getDate(), 2, "0");

	if (aWithMSeconds) {
		hms = "%1.%2".arg(hms).arg(aDate.getMilliseconds(), 3, "0");
	}

	return "%1T%2".arg(ymd).arg(hms);
};

// This function is obsolete and kept for backward compatibility only. Use faster Dir.numericPathsCompare function instead.
Str.numericPathsCompare = function numericPathsCompare(aPath1, aPath2) {
	"use strict";

	print("The function 'Str.numericPathsCompare' is obsolete. Use 'Dir.numericPathsCompare' instead.");
	var i, dir1, dir2, numDir1, numDir2,
		dirs1 = aPath1.split(/\/|\\/),
		dirs2 = aPath2.split(/\/|\\/),
		maxLen = Math.max(dirs1.length, dirs2.length);

	for (i = 0; i < maxLen; i++) {
		dir1 = dirs1[i];
		dir2 = dirs2[i];
		numDir1 = Number(dir1);
		numDir2 = Number(dir2);
		if (numDir1 && numDir2) {
			if (numDir1 < numDir2) {
				return -1;
			}
			if (numDir1 > numDir2) {
				return 1;
			}
		}
		if (dir1 < dir2) {
			return -1;
		}
		if (dir1 > dir2) {
			return 1;
		}
	}
	if (dirs1.length < dirs2.length) {
		return -1;
	}
	if (dirs1.length > dirs2.length) {
		return 1;
	}
	if (dirs1.length === dirs2.length) {
		return 0;
	}
};

Str.fileNameFromPath = function fileNameFromPath(aPath, aIncludeExt) {
	"use strict";

	var dotIdx, slashIdx;

	if (aIncludeExt === true) {
		dotIdx = aPath.length;
	} else {
		dotIdx = aPath.lastIndexOf(".");
	}

	slashIdx = Math.max(aPath.lastIndexOf("/"), aPath.lastIndexOf("\\"));
	if (slashIdx < dotIdx) {
		return aPath.substring(slashIdx + 1, dotIdx);
	}
	if (slashIdx >= dotIdx) {
		return aPath.substring(slashIdx + 1, aPath.length);
	}
};

Str.charCodeArrayToString = function charCodeArrayToString(aArray) {
	"use strict";

	var i, len,
		result = "";

	for (i = 0, len = aArray.length; i < len; i++) {
		result += String.fromCharCode(aArray[i]);
	}
	return result;
};

Str.trimStr = function trimStr(aStr) {
	"use strict";

	return aStr.trim();
};

Str.rowsToHtmlTable = function rowsToHtmlTable(aArrayOfRows, aRowLength) {
	"use strict";

	var i, j, rowString,
		tableText = "<html><head><title></title></head><body><table border=\"%1\" frame=\"box\" rules=\"none\" cellspacing=\"0\" cellpadding=\"2\" width=\"100%\">";

	if (aArrayOfRows !== undefined) {
		for (i = 0; i < aArrayOfRows.length; i++) {
			rowString = "<tr>";
			for (j = 0; j < aRowLength; j++) {
				if (aArrayOfRows[i][j] !== undefined) {
					rowString += "<td>" + aArrayOfRows[i][j].toString() + "</td> ";
				}
			}
			rowString += "</tr>";
			tableText += rowString;
		}
	}
	tableText += "</table></body></html>";
	return tableText;
};

Str.hIndexToLetter = function hIndexToLetter(aHIndex) {
	"use strict";

	switch (aHIndex) {
	case 1:
		return "'";
	case 2:
		return "''";
	case 3:
		return "'''";
	default:
		return "";
	}
};

Str.atomIndexToString = function atomIndexToString(aAtomLabel, aAtomNumber, aHIndex, aUseHIndex) {
	"use strict";

	var str = aAtomLabel;

	if (str === "") {
		str = aAtomNumber.toString();
	}
	if (aUseHIndex) {
		str += Str.hIndexToLetter(aHIndex);
	}
	return str;
};

Str.removeCriticalFilenameChars = function removeCriticalFilenameChars(aStr, aSymbol) {
	"use strict";

	return aStr.replace(/[\/?*<>:\|"\\]/g, aSymbol || "_");
};

Str.toOneWord = function toOneWord(aStr) {
	"use strict";

	var oneWordStr = aStr;

	oneWordStr.replace(' ', '_');
	oneWordStr.replace("\r\n", "\\n");
	oneWordStr.replace('\n', "\\n");
	oneWordStr.replace('\r', "\\r");
	oneWordStr.replace('\t', "\\t");
	return oneWordStr;
};

Str.getFirstLine = function getFirstLine(aStr) {
	"use strict";

	var firstLineBreak = aStr.indexOf('\n');
	if (firstLineBreak < 0) {
		return aStr;
	}
	return aStr.substring(0, firstLineBreak);
};

//! aObj: a JS object or a JSON string; aPreffix: indent charachter; aSuffix: separator character
//! properties started with @ are treated as xml attributes
//! properties with name _cdata are treated as CDATA strings var a = {_cdata="<html>...</html>"} -> <a><![CDATA[<html>...</html>]]></a>
//! var a=[val1,val2,...] is transalated as <a>val1</a><a>val2</a>....
Str.jsonToXML = function jsonToXML(aObj, aPreffix, aSuffix) {
	"use strict";

	var xml, t;

	//! aName: name of the object; aValue: its value;  aDepth: depth in the property chain; aPreffix: indent charachter; aSuffix: separator charachter
	function toXML(aName, aValue, aDepth, aPreffix, aSuffix) {
		if (aName === undefined || aValue === undefined) {
			return '';
		}
		if (aDepth === undefined) {
			aDepth = 0;
		}
		if (aPreffix === undefined) {
			aPreffix = '\t';
		}
		if (aSuffix === undefined) {
			aSuffix = '\n';
		}

		var retVal, i, attrString, childString, f,
			indent = "".rpad(aPreffix, aDepth + 1),
			valueType = typeof aValue;

		switch (valueType) {
		case 'object':
			if (aValue instanceof Array) {
				retVal = '';
				for (i = 0; i < aValue.length; ++i) {
					retVal += toXML(aName, aValue[i], aDepth, aPreffix, aSuffix);
				}
				return retVal;
			}
			attrString = '';
			childString = '';
			for (f in aValue) {
				if (aValue.hasOwnProperty(f)) {
					if (f[0] === '@') {
						attrString += '%1="%2" '.arg(f.substr(1)).arg(aValue[f]);
					} else if (f === '_cdata') {
						childString = '%2<![CDATA[%1]]>%3'.arg(aValue[f]).arg(indent).arg(aSuffix);
					} else {
						childString += toXML(f, aValue[f], aDepth + 1, aPreffix, aSuffix);
					}
				}
			}
			if (attrString.length === 0) {
				return '%3<%1>%4%2%3</%1>%4'.arg(aName).arg(childString).arg(indent).arg(aSuffix);
			}
			return '%4<%1 %2>%5%3%4</%1>%5'.arg(aName).arg(attrString).arg(childString).arg(indent).arg(aSuffix);

		default:
			return '%3<%1>%2</%1>%4'.arg(aName).arg(aValue.toString()).arg(indent).arg(aSuffix);
		}
	}

	if (typeof aObj === 'string') {
		aObj = JSON.parse(aObj);
	}
	xml = '';
	for (t in aObj) {
		if (aObj.hasOwnProperty(t)) {
			xml += toXML(t, aObj[t], 0, aPreffix, aSuffix);
		}
	}
	return xml;
};

Str.perchMolfileLabelsToNumbers = function (aMolfile) {
	'use strict';

	var perchLabelRE = /A[ ]+(\d+)\r?\n(([A-Za-z]+)(\d+)([A-Za-z]?))/,
		matchResult,
		mzzCLine,
		index = '',
		number = '';

	matchResult = perchLabelRE.exec(aMolfile);
	while (matchResult !== null) {
		//print(matchResult[0]);
		index = matchResult[1];
		number = matchResult[4];
		if (matchResult[3] === 'H') {
			if (index !== number) {
				if (matchResult[5].length) {
					number += matchResult[5].toLowerCase();
				}
				/*else {
					number += 'a';
				}*/
			}
		}
		mzzCLine = 'M  ZZC   %1 %2'.arg(index).arg(number);
		//print(matchResult[0]);
		//print(mzzCLine);
		aMolfile = aMolfile.replace(matchResult[0], mzzCLine);
		matchResult = perchLabelRE.exec(aMolfile);
	}
	//print(aMolfile);
	return aMolfile;
};


Str.specialCharactersToXMLEntities = function (aString) {
	'use strict';

	var xmlString;
	aString = aString.toString();
	xmlString = aString.replace(/\&/g, "&amp;");
	xmlString = xmlString.replace(/\"/g, "&quot;");
	xmlString = xmlString.replace(/</g, "&lt;");
	xmlString = xmlString.replace(/\>/g, "&gt;");
	xmlString = xmlString.replace(/\'/g, "&apos;");
	return xmlString;
};

// Define global functions for backward compatibility
Utl.defineGlobalProperty(this, Str, "removeNumbers");
Utl.defineGlobalProperty(this, Str, "replaceWithHTMLTag");
Utl.defineGlobalProperty(this, Str, "solventStr2Html");
Utl.defineGlobalProperty(this, Str, "firstNotEmptyString");
Utl.defineGlobalProperty(this, Str, "unBracket");
Utl.defineGlobalProperty(this, Str, "date2ISOString");
Utl.defineGlobalProperty(this, Str, "numericPathsCompare");
Utl.defineGlobalProperty(this, Str, "fileNameFromPath");
Utl.defineGlobalProperty(this, Str, "charCodeArrayToString");
Utl.defineGlobalProperty(this, Str, "trimStr");
Utl.defineGlobalProperty(this, Str, "rowsToHtmlTable");
Utl.defineGlobalProperty(this, Str, "hIndexToLetter");
Utl.defineGlobalProperty(this, Str, "atomIndexToString");
Utl.defineGlobalProperty(this, Str, "removeCriticalFilenameChars");
Utl.defineGlobalProperty(this, Str, "toOneWord");
Utl.defineGlobalProperty(this, Str, "getFirstLine");
Utl.defineGlobalProperty(this, Str, "jsonToXML");
